package com.example.service;

import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import com.example.utils.DataChange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/****
 *
 * 基于文件共享的形式实现服务端的消息传递
 */
public class ShareFileClusterServiceImpl implements ClusterService, Watcher {

    //标记本节点插入的消息，避免重复消费
    private final Map<String, String> pushMap = new ConcurrentHashMap<>();
    Logger logger = LoggerFactory.getLogger(getClass());

    private String logPath;

    private WatchMonitor watchMonitor;

    private IndexService indexService;

    private ApplicationContext applicationContext;

    @Override
    public void notifyToMessage(DataChange dataChange) {
        this.pushMap.put(dataChange.getId(), dataChange.getId());
        ObjectOutputStream oos = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(this.logPath + File.separator + dataChange.getId());
            oos = new ObjectOutputStream(fileOutputStream);
            oos.writeObject(dataChange);
            oos.flush();
        } catch (Exception e) {
            logger.warn("对象序列化到磁盘出错: " + e.getMessage());
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    @Override
    public void start(ApplicationContext applicationContext, IndexService indexService) {
        this.applicationContext = applicationContext;
        this.indexService = indexService;
        this.logPath = applicationContext.getEnvironment().getProperty("sitc.log.path");
        this.watchMonitor = WatchMonitor.create(new File(logPath), WatchMonitor.EVENTS_ALL);
        this.watchMonitor.setWatcher(this);
        this.watchMonitor.start();
    }

    @Override
    public void stop() {
        this.watchMonitor.close();
    }

    @Override
    public void onCreate(WatchEvent<?> event, Path currentPath) {

    }

    @Override
    public void onModify(WatchEvent<?> event, Path currentPath) {
        String fileName = event.context().toString();
        if (!this.pushMap.containsKey(fileName)) {
            ObjectInputStream ois = null;
            try {
                File file = new File(logPath + File.separator + fileName);
                ois = new ObjectInputStream(Files.newInputStream(file.toPath()));
                DataChange object = (DataChange) ois.readObject();
                indexService.onMessage(object);
            } catch (Exception e) {
                logger.warn("读取消息文件: " + fileName + " 出錯: " + e.getMessage());
            } finally {
                if (ois != null) {
                    try {
                        ois.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
    }

    @Override
    public void onDelete(WatchEvent<?> event, Path currentPath) {

    }

    @Override
    public void onOverflow(WatchEvent<?> event, Path currentPath) {

    }

    public String getLogPath() {
        return logPath;
    }

    public void setLogPath(String logPath) {
        this.logPath = logPath;
    }

    public WatchMonitor getWatchMonitor() {
        return watchMonitor;
    }

    public void setWatchMonitor(WatchMonitor watchMonitor) {
        this.watchMonitor = watchMonitor;
    }

    public IndexService getIndexService() {
        return indexService;
    }

    public void setIndexService(IndexService indexService) {
        this.indexService = indexService;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}
